package model.datastructure;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * This is an implementation of an 2 dimensional field.
 * 
 * @version 0.1 11/26/10
 * @author Ingmar te Raa
 * 
 * @see AbstractField
 */
public class Field2D extends AbstractField {

	private List<Block2D> mPlacedBlocks;
	private LinkedList<char[]> mStructure;
	private Dimension2D mFieldDimension;
	private String mDisplayValues = "abcdefghijklmnopqrstuvwxyz";
	private int mBlockCounter;
	private final char emptyChar = '.';

	/**
	 * Create a new <code>Field2D</code>.
	 * 
	 * @param dimension
	 *            - the dimension of the field.
	 */
	public Field2D( Dimension2D dimension ) {
		super( dimension );
		/* Init the values */
		mFieldDimension = dimension;
		mPlacedBlocks = new ArrayList<Block2D>();
		mStructure = new LinkedList<char[]>();
		mBlockCounter = 0;
	}

	/**
	 * Place a block in the field.
	 * 
	 * @param abstractBlock
	 *            - the block witch needs to be placed.
	 * @see Block2D
	 */
	public void placeBlock( AbstractBlock abstractBlock ) throws RuntimeException {

		/* Check if the block is instance of Block2D */
		if( ! ( abstractBlock instanceof Block2D ) ) {
			throw new RuntimeException( "Can not place a block other than Block2D" );
		}

		/* Gather the information used in the placement */
		Block2D block = ( Block2D ) abstractBlock;
		Position2D blockPosition = block.getPosition();
		BlockType2D blockType = block.getBlockType();
		Dimension2D blockDimension = blockType.getDimension( block.rotation );

		/* Check if the field height is high enough. */
		if( blockPosition.getY() + blockDimension.getHeight() > mFieldDimension.getHeight() ) {

			addRows( blockPosition.getY() + blockDimension.getHeight() - mFieldDimension.getHeight() );
		}
		/* Walk trough the block structure */
		for( int x = 0; x < blockDimension.getWidth(); x ++ ) {
			for( int y = 0; y < blockDimension.getHeight(); y ++ ) {
				if (blockType.getCharAt( x, y, block.getRotation() ) == 1) {
					/* Check if the place is empty. */
					if( mStructure.get( blockPosition.getY() + y )[blockPosition.getX() + x] != emptyChar  ) {
						throw new RuntimeException( "Place was already taken, illegal placeblock call (" + block.toString() + ")" );
					} else {
						/* Fill the place. */
						mStructure.get( blockPosition.getY() + y )[blockPosition.getX() + x] = mDisplayValues.charAt( mBlockCounter % mDisplayValues.length() );
					}
				}
			}
		}
		/* Add the block */
		mPlacedBlocks.add( block );
		mBlockCounter ++ ;
	}

	/**
	 * Add some rows to the field.
	 * 
	 * @param rows
	 */
	private void addRows( int rows ) {

		/* Update the field dimension of the field. */
		mFieldDimension.setHeight( mFieldDimension.getHeight() + rows );
		for(int i = 0; i < rows; i++) {
			/* Add a new row */
			char[] row = new char[mFieldDimension.getWidth()];
			for (int j = 0; j < row.length; j++) {
				row[j] = emptyChar;
			}
			mStructure.add( row );
		}
	}

	/**
	 * Get the dimension of this field.
	 * 
	 * @return Dimension2D
	 */
	public Dimension2D getDimension() {
		return mFieldDimension;
	}

	/**
	 * Get the surface of this field.
	 */
	@Override
	public FieldSurface2D getSurface() {
		return ( new FieldSurface2D( this ) );
	}

	/**
	 * Get a character at a given position.
	 * 
	 * @param indexX
	 * @param indexY
	 * @return
	 */
	public char getCharAt( int indexX, int indexY ) {
		//System.out.println("charat: " + indexX + ", " + indexY);
		return mStructure.get( indexY )[indexX];
	}

	/**
	 * @return the emptyChar
	 */
	public char getEmptyChar() {
		return emptyChar;
	}
	
	/**
	 * Returns a string representation from the field
	 */
	public String toString() {
		/* The return String */
		String fieldString = "";
		
		/* We're going to loop through the field from the top to the bottom */
		for( int indexY = mStructure.size() - 1; indexY >= 0; indexY-- ) {
			
			/* Loop left to right */
			for( int indexX = 0; indexX < mStructure.get(indexY).length; indexX++ ) {
				fieldString += ""+ getCharAt(indexX, indexY);
			}
			
			/* Lines are made to be broken.. */
			fieldString += "\n";
		}
		
		/* There ya go */
		return fieldString;
	}

}